/*
 * TagCompound.cpp
 *
 *  Created on: 19 juin 2011
 *      Author: Pierrick Caillon <pierrick@megami.fr>
 */

#include "TagCompound.h"
#include "TagFactory.h"
#include "TagByte.h"
#include "TagEnd.h"

/* NAMEDTAG_H_ */
/* NAMEDTAG_H_ */
namespace NamedBinaryTag
{
TagCompound::TagCompound() :
	NamedTag()
{
}

TagCompound::~TagCompound()
{
	this->Clear();
}

void TagCompound::Read(std::istream & input, bool ignoreName)
{
	NamedTag::Read(input, ignoreName);
	std::vector<ITag*> items;
	TagByte type;
	while (true)
	{
		type.Read(input, true);
		ITag *element = TagFactory::CreateTag((TagTypes) (type.get_Value()));
		if (element->get_TagType() == TAG_END)
		{
			TagFactory::DeleteTag(element);
			break;
		}
		element->root = this;
		element->Read(input);
		items.push_back(element);
	}

	this->Clear();
	this->items.swap(items);
	for (std::vector<ITag*>::const_iterator it = items.begin(); it
			!= items.end(); it++)
	{
		TagFactory::DeleteTag(*it);
	}
}

ITag *TagCompound::operator [](std::string name) const
{
	return this->get_Item(name);
}

ITag *TagCompound::get_Item(std::string name) const
{
	for (std::vector<ITag*>::const_iterator it = this->items.begin(); it
			!= this->items.end(); it++)
		if ((*it)->get_Name() == name)
			return *it;

	return NULL;
}

bool TagCompound::Add(ITag *tag)
{
	if (tag == NULL)
		std::__throw_invalid_argument(
				__N("TagCompound::Add: tag argument is NULL."));
	if (tag->get_TagType() == TAG_END)
		std::__throw_invalid_argument(
				__N("TagCompound::Add: TagEnd is not usable."));
	if (this->ContainsKey(tag->get_Name()))
		return false;

	if ((tag->root != NULL) && (tag->root != this))
		std::__throw_invalid_argument(
				__N("TagCompound::Add: Trying to add a Tag neither unbound nor bound to this container."));
	this->items.push_back(tag);
	return true;
}

bool TagCompound::Remove(std::string name)
{
	for (std::vector<ITag*>::iterator it = this->items.begin(); it
			!= this->items.end(); it++)
		if ((*it)->get_Name() == name)
		{
			TagFactory::DeleteTag(*it);
			this->items.erase(it);
			return true;
		}

	return false;
}

void TagCompound::Clear()
{
	for (std::vector<ITag*>::const_iterator it = this->items.begin(); it
			!= this->items.end(); it++)
	{
		TagFactory::DeleteTag(*it);
	}
	this->items.clear();
}

void TagCompound::Write(std::ostream & output, bool ignoreName) const
{
	NamedTag::Write(output, ignoreName);
	TagByte type;
	for (std::vector<ITag*>::const_iterator it = this->items.begin(); it
			!= this->items.end(); it++)
	{
		type.set_Value((*it)->get_TagType());
		type.Write(output, true);
		(*it)->Write(output);
	}
	TagEnd end;
	type.set_Value(end.get_TagType());
	type.Write(output, true);
}

size_t TagCompound::get_Count() const
{
	return this->items.size();
}

std::vector<std::string> TagCompound::get_Keys() const
{
	std::vector<std::string> keys;
	for (std::vector<ITag*>::const_iterator it = this->items.begin(); it
			!= this->items.end(); it++)
		keys.push_back((*it)->get_Name());

	return keys;
}
bool TagCompound::Remove(ITag *tag)
{
	for (std::vector<ITag*>::iterator it = this->items.begin(); it
			!= this->items.end(); it++)
		if (*it == tag)
		{
			this->items.erase(it);
			return true;
		}

	return false;
}

bool TagCompound::ContainsKey(std::string name)
{
	for (std::vector<ITag*>::iterator it = this->items.begin(); it
			!= this->items.end(); it++)
		if ((*it)->get_Name() == name)
			return true;

	return false;
}
ITag *TagCompound::clone() const
{
	return this->clone(NULL);
}
TagTypes TagCompound::get_TagType() const
{
	return TAG_COMPOUND;
}

ITag *TagCompound::clone(void *newRoot) const
{
	TagCompound *copy = new TagCompound(newRoot);
	for (std::vector<ITag*>::const_iterator it = this->items.begin(); it
			!= this->items.end(); it++)
		copy->Add((*it)->clone(newRoot));

	return copy;
}

}
